/*
 * Copyright (C) 2017 zhouyou(478319399@qq.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.zhouyou.view.segmentedbar;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.render.*;
import ohos.agp.text.SimpleTextLayout;
import ohos.agp.utils.*;
import ohos.app.Context;
import ohos.media.image.PixelMap;

import java.text.DecimalFormat;
import java.util.List;

/**
 * <p>描述：分段控件</p>
 * 作者： zhouyou<br>
 * 日期： 2015/12/18 13:40<br>
 * 版本： v1.0<br>
 * <p/>
 * 修改者： zhouyou  日期:2016/12/01 13:40<br>
 * 1.可以支持设置背景、渐变背景<br>
 * 2.可以支持分段设置渐变背景，每段的渐变颜色都可以不一样<br>
 * 3.支持可以设置两种模式的标记 sbv_sliderType  Sign/Cover 具体看代码<br>
 * 4.可以支持设置Slider的图片sbv_sliderImg<br>
 * 5.支持底部描述符文字segment 中间对齐和两端对齐  两端对齐的文字描述符需要用“&”符合分割  sbv_descriptionAlign<br>
 * 6.可以支持设置顶部描述符文字segment 中间对齐和两端对齐  两端对齐的文字描述符需要用“&”符合分割  sbv_descriptionAlign<br>
 */
public class SegmentedBarView extends Component implements Component.DrawTask, Component.EstimateSizeListener {
    private int[] segmentBgColors = new int[]{Color.RED.getValue(), Color.BLUE.getValue()}; // 设置渐变的背景颜色
    private int[] segmentProgressColors = new int[]{Color.BLUE.getValue(), Color.YELLOW.getValue()}; // 设置渐变的背景颜色
    private RectFloat roundRectangleBounds;
    private Paint fillPaint;
    private Paint signPaint;
    private Paint signborderPaint;

    private int valueSignHeight; // 滑块的高度
    private int valueSignWidth; // 滑块的宽度
    private int valueSignColor; // 滑块的颜色
    private Float value; // 滑块显示的数值
    private String valueText; // 滑块内显示的文本如果设置了valueText，value会自动不起作用
    private Integer valueSegment; // 表示选中第几个
    private String valueSegmentText; // 每段分段控件上显示的内容
    private Rect rectBounds;
    private Rect valueSignBounds;
    private int valueSignRound; // 滑块的圆角大小
    private float valueSignCenter = -1;
    private int currentBarColor;

    private int emptySegmentColor; // 没有数据时的颜色
    private int arrowHeight; // 滑块箭头的高度
    private int arrowWidth; // 滑块箭头的宽度
    private int gapWidth; // 分段控件之间的间隔
    private int gapColor; // 分段控件之间的间隔颜色
    private int barHeight; // 分段条的高度

    private List<Segment> segments; // 分段控件集合
    private String unit;
    private Paint emptyPaint;
    private Paint grapPaint;
    private Paint progressPaint;
    private DecimalFormat formatter;
    private String emptySegmentText; // 分段控件数据为空时显示的内容，List<Segment> segments 为null或者size是0
    private int barRoundingRadius = 0;

    private boolean showSegmentText; // 是否显示分段控件上的文本
    private int sideStyle = SegmentedBarViewSideStyle.ROUNDED; // 设置分段控件两边的样式
    private int sideTextStyle = SegmentedBarViewSideTextStyle.ONE_SIDED;
    private static final String[] EMPTY_STRING_ARRAY = new String[]{};

    private int segmentTextSize; // 分段控件文字字体大小
    private int segmentTextColor = Color.WHITE.getValue(); // 分段控件文字字体颜色
    private Paint segmentTextPaint;

    private int valueTextSize; // 滑块数值文字字体大小
    private int valueTextColor = Color.WHITE.getValue(); // 滑块文字字体颜色，默认白色
    private SimpleTextLayout valueTextLayout;
    private Paint valueTextPaint; // 滑块数值文本

    private int descriptionTextColor = Color.DKGRAY.getValue(); // 描述文字字体颜色，默认灰色
    private int descriptionTopTextColor = -1; // 描述文字字体颜色，默认灰色
    private int descriptionHighlightTextColor = 65536; // 描述文字高亮字体颜色，默认：descriptionTextColor
    private int descriptionAlign; // bar条目底部描述文本对齐方式
    private int descriptionTopAlign; // bar条目顶部描述文本对齐方式
    private int descriptionTextSize; // 描述文字字体大小
    private int descriptionHightlightTextSize = -1; // 描述文字高亮字体大小
    private int descriptionBoxHeight; // 底部描述文本所占的高度
    private int descriptionBoxTopHeight; // 底部描述文本距分段条的距离
    private Paint descriptionTextPaint;
    private static final int ALIGN_CENTER = 0; // 描述文字对齐方式-居中
    private static final int ALIGN_BOTH = 1; // 描述文字对齐方式-两边
    private boolean showDescriptionText; // 是否显示分段控件下方的描述文本
    private boolean showDescriptionTopText; // 是否显示分段控件顶部的描述文本

    private int sliderType; // 滑块的类型：sign:标记  slider:滑块
    private PixelMapElement sliderImage; // COVER类型，覆盖图片
    private static final int SIGN = 0; // 滑块在分段控件上面-非叠加的展现形式
    private static final int SLIDER = 1; // 滑块在分段控件上部显示-叠加的展现形式
    private static final int CUSTOM = 2; // 滑块在分段控件上部显示-叠加的展现形式 采用自定义

    private Path trianglePath;
    private Path triangleboderPath;

    private Point point1;
    private Point point2;
    private Point point3;
    private Rect segmentRect;
    private boolean isDrawSegmentBg = false;
    private boolean isDrawSegmentProgress = false;
    private int value_sign_border_size;
    private boolean show_sign_boder;
    private int sideRule; // 设置分段规则
    private int value_sign_border_color;

    private PixelMap mBitmap;
    private int thembH, thembW;
    private boolean isShowThumb;

    /**
     * 初始化
     *
     * @param context context
     */
    public SegmentedBarView(Context context) {
        super(context);
        init(context, null);
    }

    /**
     * 初始化
     *
     * @param context context
     * @param attrs attrs
     */
    public SegmentedBarView(Context context, AttrSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    /**
     * 初始化
     *
     * @param context context
     * @param attrs attrs
     * @param styleName styleName
     */
    public SegmentedBarView(Context context, AttrSet attrs, String styleName) {
        super(context, attrs, styleName);
        init(context, attrs);
    }

    /**
     * 初始化
     *
     * @param context context
     * @param attrs attrs
     * @param resId resid
     */
    public SegmentedBarView(Context context, AttrSet attrs, int resId) {
        super(context, attrs, resId);
        init(context, attrs);
    }

    private void init(Context context, AttrSet attrs) {
        sliderImage = (PixelMapElement) AttrUtils.getElementValueByAttr(attrs, "sbv_sliderImg", null);
        sliderType = AttrUtils.getIntValueByAttr(attrs, "sbv_sliderType", SIGN);
        descriptionAlign = AttrUtils.getIntValueByAttr(attrs, "sbv_descriptionAlign", ALIGN_CENTER);
        descriptionTopAlign = AttrUtils.getIntValueByAttr(attrs, "sbv_descriptionTopAlign", ALIGN_CENTER);
        segmentTextSize = AttrUtils.getDimensionValueByAttr(attrs, "sbv_segment_text_size", 40);
        valueTextSize = AttrUtils.getDimensionValueByAttr(attrs, "sbv_value_text_size", 14);
        descriptionTextSize = AttrUtils.getDimensionValueByAttr(attrs, "sbv_description_text_size", 14);
        descriptionHightlightTextSize = AttrUtils.getDimensionValueByAttr(attrs, "sbv_description_hightlight_text_size", -1);
        barHeight = AttrUtils.getDimensionValueByAttr(attrs, "sbv_bar_height", 24);
        valueSignHeight = AttrUtils.getDimensionValueByAttr(attrs, "sbv_value_sign_height", 32);
        valueSignWidth = AttrUtils.getDimensionValueByAttr(attrs, "sbv_value_sign_width", 72);
        arrowHeight = AttrUtils.getDimensionValueByAttr(attrs, "sbv_arrow_height", 5);
        arrowWidth = AttrUtils.getDimensionValueByAttr(attrs, "sbv_arrow_width", 10);
        gapWidth = AttrUtils.getDimensionValueByAttr(attrs, "sbv_segment_gap_width", 2);
        gapColor = AttrUtils.getColorValueByAttr(attrs, "sbv_segment_gap_color", new Color(Color.getIntColor("#ffffff"))).getValue();
        valueSignRound = AttrUtils.getDimensionValueByAttr(attrs, "sbv_value_sign_round", 4);
        descriptionBoxHeight = AttrUtils.getDimensionValueByAttr(attrs, "sbv_description_box_height", 24);
        descriptionBoxTopHeight = AttrUtils.getDimensionValueByAttr(attrs, "sbv_description_box_top_height", 24);
        value_sign_border_size = AttrUtils.getDimensionValueByAttr(attrs, "sbv_value_sign_border_size", 2);
        thembH = AttrUtils.getDimensionValueByAttr(attrs, "sbv_thumb_h", (int) (barHeight * 1.3f));
        thembW = AttrUtils.getDimensionValueByAttr(attrs, "sbv_thumb_w", (int) (barHeight * 1.3f));
        showSegmentText = AttrUtils.getBooleanValueByAttr(attrs, "sbv_show_segment_text", true);
        showDescriptionText = AttrUtils.getBooleanValueByAttr(attrs, "sbv_show_description_text", false);
        showDescriptionTopText = AttrUtils.getBooleanValueByAttr(attrs, "sbv_show_description_top_text", false);

        if (valueSegmentText == null) {
            valueSegmentText = AttrUtils.getStringValueByAttr(attrs, "sbv_value_segment_text", "Current Value");
        }
        if (emptySegmentText == null) {
            emptySegmentText = AttrUtils.getStringValueByAttr(attrs, "sbv_empty_segment_text", "Empty");
        }
        isDrawSegmentBg = AttrUtils.getBooleanValueByAttr(attrs, "sbv_segment_bg", false);
        isDrawSegmentProgress = AttrUtils.getBooleanValueByAttr(attrs, "sbv_segment_progress", false);
        show_sign_boder = AttrUtils.getBooleanValueByAttr(attrs, "sbv_show_sign_boder", false);
        value_sign_border_color = AttrUtils.getColorValueByAttr(attrs, "sbv_value_sign_border_color", new Color(Color.getIntColor("#7492E2"))).getValue();
        descriptionTextColor = AttrUtils.getColorValueByAttr(attrs, "sbv_description_text_color", new Color(descriptionTextColor)).getValue();
        descriptionTopTextColor = AttrUtils.getColorValueByAttr(attrs, "sbv_description_top_text_color", new Color(descriptionTextColor)).getValue();
        descriptionHighlightTextColor = AttrUtils.getColorValueByAttr(attrs, "sbv_description_highlight_text_color", new Color(65536)).getValue();
        int segment_bg_startcolor = AttrUtils.getColorValueByAttr(attrs, "sbv_segment_bg_startcolor", new Color(Color.getIntColor("#FF4444"))).getValue();
        int segment_end_endtcolor = AttrUtils.getColorValueByAttr(attrs, "sbv_segment_bg_endcolor", new Color(Color.getIntColor("#99CC00"))).getValue();
        segmentBgColors[0] = segment_bg_startcolor;
        segmentBgColors[1] = segment_end_endtcolor;
        int segment_progress_startcolor = AttrUtils.getColorValueByAttr(attrs, "sbv_segment_progress_startcolor", new Color(Color.getIntColor("#669900"))).getValue();
        int segment_progress_endcolor = AttrUtils.getColorValueByAttr(attrs, "sbv_segment_progress_endcolor", new Color(Color.getIntColor("#0099CC"))).getValue();
        segmentProgressColors[0] = segment_progress_startcolor;
        segmentProgressColors[1] = segment_progress_endcolor;
        valueSignColor = AttrUtils.getColorValueByAttr(attrs, "sbv_value_sign_background", new Color(Color.getIntColor("#7492E2"))).getValue();
        emptySegmentColor = AttrUtils.getColorValueByAttr(attrs, "sbv_empty_segment_background", new Color(Color.getIntColor("#858585"))).getValue();
        sideStyle = AttrUtils.getIntValueByAttr(attrs, "sbv_side_style", SegmentedBarViewSideStyle.ROUNDED);
        sideTextStyle = AttrUtils.getIntValueByAttr(attrs, "sbv_side_text_style", SegmentedBarViewSideTextStyle.ONE_SIDED);
        sideRule = AttrUtils.getIntValueByAttr(attrs, "sbv_segment_rule", SegmentedBarViewSideRule.AVERAGE);

        if (sliderImage != null || sliderType == CUSTOM) {
            isShowThumb = true;
        }
        if (isShowThumb) {
            if (sliderType == SLIDER) {
                mBitmap = sliderImage.getPixelMap();
                thembH = mBitmap.getImageInfo().size.width;
                thembW = mBitmap.getImageInfo().size.height;
            }
        }

        formatter = new DecimalFormat("##.####");

        segmentTextPaint = new Paint();
        segmentTextPaint.setColor(Color.WHITE);
        segmentTextPaint.setStyle(Paint.Style.FILL_STYLE);

        valueTextPaint = new Paint();
        valueTextPaint.setColor(Color.WHITE);
        valueTextPaint.setStyle(Paint.Style.FILL_STYLE);
        valueTextPaint.setTextSize(valueTextSize);
        valueTextPaint.setColor(new Color(valueTextColor));

        descriptionTextPaint = new Paint();
        descriptionTextPaint.setColor(Color.DKGRAY);
        descriptionTextPaint.setStyle(Paint.Style.FILL_STYLE);

        fillPaint = new Paint();
        fillPaint.setStyle(Paint.Style.FILL_STYLE);
        fillPaint.setAntiAlias(true);

        signPaint = new Paint();
        signPaint.setStyle(Paint.Style.FILL_STYLE);
        signPaint.setAntiAlias(true);

        signborderPaint = new Paint();
        signborderPaint.setStyle(Paint.Style.STROKE_STYLE);
        signborderPaint.setStrokeWidth(value_sign_border_size);
        signborderPaint.setColor(new Color(value_sign_border_color));
        signborderPaint.setAntiAlias(true);

        emptyPaint = new Paint();
        emptyPaint.setStyle(Paint.Style.FILL_STYLE);
        emptyPaint.setAntiAlias(true);

        progressPaint = new Paint();
        progressPaint.setStyle(Paint.Style.FILL_STYLE);
        progressPaint.setAntiAlias(true);

        grapPaint = new Paint();
        grapPaint.setStyle(Paint.Style.FILL_STYLE);

        rectBounds = new Rect();
        roundRectangleBounds = new RectFloat();
        valueSignBounds = new Rect();
        segmentRect = new Rect();

        trianglePath = new Path();
        trianglePath.setFillType(Path.FillType.EVEN_ODD);

        triangleboderPath = new Path();

        point1 = new Point();
        point2 = new Point();
        point3 = new Point();
        if (sliderType == SLIDER || sliderType == CUSTOM) {
            arrowHeight = 0;
            valueSignHeight = 0;
        }

        if (!showDescriptionTopText) {
            descriptionBoxTopHeight = 0;
        }
        setEstimateSizeListener(this);
        addDrawTask(this);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        valueSignCenter = -1;
        if (isDrawSegmentBg) { // 是否画背景 默认：不画 支持渐变，如果不需要渐变，startColor和endColor设置一样
            drawBgSegment(canvas);
        }

        if (isDrawSegmentProgress) { // 是否画进度，支持渐变，如果不需要渐变，startColor和endColor设置一样
            drawProgressSegment(canvas);
        }

        int segmentsSize = segments == null ? 0 : segments.size();
        if (segmentsSize > 0) {
            Segment lastSegment = segments.get(segments.size() - 1);
            for (int segmentIndex = 0; segmentIndex < segmentsSize; segmentIndex++) { // 画分段控件
                Segment segment = segments.get(segmentIndex);
                drawSegment(canvas, segment, segmentIndex, segmentsSize, lastSegment.getMaxValue());
            }
        } else { // 显示空数据
            drawEmptySegment(canvas);
        }

        if (!valueIsEmpty()) { // 判断是否有数据
            switch (sliderType) {
                case SIGN: // 设置标记
                    drawValueSign(canvas, valueSignSpaceHeight(), (int) valueSignCenter);
                    break;
                case SLIDER: // 设置滑块 覆盖图片
                    if (!isShowThumb) {
                        throw new RuntimeException("plase add sbv_sliderImg!!!!!");
                    }
                    drawSliderImg(canvas, mBitmap, thembW, thembH, valueSignCenter, rectBounds.top - getXtop() + thembH / 2, signPaint);
                    break;
                case CUSTOM: // 设置滑块 自定义覆盖
                    drawCustomThumb(canvas, thembW, thembH, valueSignCenter, rectBounds.top - getXtop() + thembH / 2, currentBarColor, signPaint);
                    break;
                default:
                    break;
            }
        }
    }

    /**
     * 计算滑块为图片或者自定义模式时，两边间距
     *
     * @return getXLeft
     */
    private int getXLeft() {
        return ((sliderType == SLIDER || sliderType == CUSTOM) && isShowThumb && valueSegment == null) ? thembW / 2 : 0;
    }

    /**
     * 计算滑块为图片或者自定义模式时，bar条和thumb高度差
     *
     * @return getXtop
     */
    private int getXtop() {
        int xtop = thembH - barHeight > 0 ? (thembH - barHeight) / 2 : 0;
        return ((sliderType == SLIDER || sliderType == CUSTOM) && isShowThumb) ? xtop : 0;
    }

    /**
     * 自定义分段thumb 图片指示---如果想自定义标记，可以继承SegmentedBarView，重写drawSliderImg方法
     *
     * @param canvas  画板
     * @param bitmap  图片对应的bitmap
     * @param thumbW  bar条覆盖物标记宽度(图片的宽度)
     * @param thumbH  bar条覆盖物标记高度(图片的高度)
     * @param centerX 当前进度所在的中心点x坐标
     * @param centerY 当前进度所在的中心点y坐标
     * @param paint   画笔paint
     */
    private void drawSliderImg(Canvas canvas, PixelMap bitmap, int thumbW, int thumbH, float centerX, float centerY, Paint paint) {
        canvas.drawPixelMapHolder(new PixelMapHolder(bitmap), centerX - (float) thumbW / 2, centerY - (float) thumbH / 2, paint);
    }

    /**
     * 自定义分段thumb指示---如果想自定义标记，可以继承SegmentedBarView，重写drawCustomThumb方法
     *
     * @param canvas  画板
     * @param thumbW  bar条覆盖物标记宽度
     * @param thumbH  bar条覆盖物标记高度
     * @param centerX 当前进度所在的中心点x坐标
     * @param centerY 当前进度所在的中心点Y坐标
     * @param color   当前标记所在位置对应的bar条区间颜色
     * @param paint   画笔paint
     */
    protected void drawCustomThumb(Canvas canvas, int thumbW, int thumbH, float centerX, float centerY, int color, Paint paint) {
        paint.setColor(new Color(color));
        int r = Math.max(thumbH, thumbW) / 2;
        paint.setMaskFilter(new MaskFilter(5, MaskFilter.Blur.SOLID));
        canvas.drawCircle(centerX, centerY, r, paint);
        paint.setMaskFilter(null);
    }

    // 显示空数据
    private void drawEmptySegment(Canvas canvas) {
        int segmentsSize = 1;
        float singleSegmentWidth = (float) getContentWidth() / segmentsSize;
        rectBounds.set(getPaddingLeft(), valueSignSpaceHeight() + getPaddingTop() + descriptionBoxTopHeight() + getXtop(), (int)
                singleSegmentWidth + getPaddingLeft(), barHeight + valueSignSpaceHeight() + getPaddingTop()
                + descriptionBoxTopHeight() + getXtop());
        emptyPaint.setColor(new Color(emptySegmentColor));
        barRoundingRadius = rectBounds.getHeight() / 2;
        if (barRoundingRadius > singleSegmentWidth / 2) {
            sideStyle = SegmentedBarViewSideStyle.NORMAL;
        }

        segmentRect.modify(rectBounds);

        switch (sideStyle) {
            case SegmentedBarViewSideStyle.ROUNDED:
                roundRectangleBounds.modify(rectBounds.left, rectBounds.top, rectBounds.right, rectBounds.bottom);
                canvas.drawRoundRect(roundRectangleBounds, barRoundingRadius, barRoundingRadius, emptyPaint);
                break;
            case SegmentedBarViewSideStyle.ANGLE:
                rectBounds.set(barRoundingRadius + getPaddingLeft(),
                        valueSignSpaceHeight() + descriptionBoxTopHeight() + getPaddingTop() + getXtop(),
                        getWidth() - getPaddingRight() - barRoundingRadius,
                        barHeight + valueSignSpaceHeight() + descriptionBoxTopHeight() + getPaddingTop() + getXtop());
                canvas.drawRect(
                        rectBounds,
                        emptyPaint
                );
                // Draw left triangle
                point1 = new Point(rectBounds.left - barRoundingRadius, rectBounds.top + barRoundingRadius);
                point2 = new Point(rectBounds.left, rectBounds.top);
                point3 = new Point(rectBounds.left, rectBounds.bottom);

                drawTriangle(canvas, point1, point2, point3, emptyPaint);

                // Draw right triangle
                point1 = new Point(rectBounds.right + barRoundingRadius, rectBounds.top + barRoundingRadius);
                point2 = new Point(rectBounds.right, rectBounds.top);
                point3 = new Point(rectBounds.right, rectBounds.bottom);

                drawTriangle(canvas, point1, point2, point3, emptyPaint);
                break;
            case SegmentedBarViewSideStyle.NORMAL:
                canvas.drawRect(
                        rectBounds,
                        emptyPaint
                );
            default:
                break;
        }

        if (showSegmentText) {
            String textToShow;
            textToShow = emptySegmentText;
            segmentTextPaint.setTextSize(segmentTextSize);
            drawTextCentredInRectWithSides(canvas, segmentTextPaint, textToShow, segmentRect.left, segmentRect.top,
                    segmentRect.right, segmentRect.bottom);
        }
    }

    // 是否需要画背景
    private void drawProgressSegment(Canvas canvas) {
        if (sideRule != SegmentedBarViewSideRule.SCALE) { // 按比例分
            return;
        }

        if (segments == null || segments.isEmpty()) {
            return; // 没有进度
        }
        float minValue = segments.get(0).getMinValue();
        float maxValue = segments.get(segments.size() - 1).getMaxValue();
        if (!(value != null && (value >= minValue && value <= maxValue))) {
            return;
        }

        int index = 0;
        for (int i = 0; i < segments.size(); i++) {
            if (value >= segments.get(i).getMinValue() && value <= segments.get(i).getMaxValue()) {
                index = i;
                break;
            }
        }

        float valueSignCenterPercent = (value - minValue) / (maxValue - minValue);
        float spacing = maxValue - minValue;
        float singleSegmentWidth = getContentWidth() * spacing / maxValue;
        float valueSignCenter = (int) (getPaddingLeft() + getXLeft() + valueSignCenterPercent * singleSegmentWidth);
        float fraction = valueSignCenterPercent;
        int new_color = getCurrentColor(fraction, segmentProgressColors[0], segmentProgressColors[1]); // 根据进度动态计算结束颜色
        rectBounds.set(getPaddingLeft() + getXLeft(), valueSignSpaceHeight() + getPaddingTop() + descriptionBoxTopHeight() + getXtop(), (int)
                valueSignCenter + thembW / 3 + gapWidth * index, barHeight + valueSignSpaceHeight() + getPaddingTop() + descriptionBoxTopHeight() + getXtop());

        Point[] pointArrays = new Point[]{new Point(rectBounds.left, rectBounds.top),
                new Point(rectBounds.right, rectBounds.bottom)};
        LinearShader shader = new LinearShader(pointArrays, null,
                new Color[]{new Color(segmentProgressColors[0]), new Color(new_color)}, Shader.TileMode.MIRROR_TILEMODE);
        progressPaint.setShader(shader, Paint.ShaderType.LINEAR_SHADER);

        barRoundingRadius = rectBounds.getHeight() / 2;
        if (barRoundingRadius > singleSegmentWidth / 2) {
            sideStyle = SegmentedBarViewSideStyle.NORMAL;
        }

        segmentRect.modify(rectBounds);

        switch (sideStyle) {
            case SegmentedBarViewSideStyle.ROUNDED:
                roundRectangleBounds.modify(rectBounds.left, rectBounds.top, rectBounds.right, rectBounds.bottom);
                canvas.drawRoundRect(roundRectangleBounds, barRoundingRadius, barRoundingRadius, progressPaint);
                break;
            case SegmentedBarViewSideStyle.ANGLE:
                rectBounds.set(barRoundingRadius + getPaddingLeft(),
                        valueSignSpaceHeight() + descriptionBoxTopHeight() + getPaddingTop(),
                        getWidth() - getPaddingRight() - barRoundingRadius,
                        barHeight + valueSignSpaceHeight() + descriptionBoxTopHeight() + getPaddingTop());
                canvas.drawRect(rectBounds, progressPaint
                );
                // Draw left triangle
                point1 = new Point(rectBounds.left - barRoundingRadius, rectBounds.top + barRoundingRadius);
                point2 = new Point(rectBounds.left, rectBounds.top);
                point3 = new Point(rectBounds.left, rectBounds.bottom);

                drawTriangle(canvas, point1, point2, point3, progressPaint);

                // Draw right triangle
                point1 = new Point(rectBounds.right + barRoundingRadius, rectBounds.top + barRoundingRadius);
                point2 = new Point(rectBounds.right, rectBounds.top);
                point3 = new Point(rectBounds.right, rectBounds.bottom);

                drawTriangle(canvas, point1, point2, point3, progressPaint);
                break;
            case SegmentedBarViewSideStyle.NORMAL:
                canvas.drawRect(rectBounds, progressPaint);
            default:
                break;
        }
    }

    // 是否需要画背景
    private void drawBgSegment(Canvas canvas) {
        int segmentsSize = 1;
        float singleSegmentWidth = (float) getContentWidth() / segmentsSize;
        rectBounds.set(getPaddingLeft() + getXLeft(), valueSignSpaceHeight() + getPaddingTop() + descriptionBoxTopHeight() + getXtop(), (int)
                getPaddingLeft() + getXLeft() + getContentWidth(), barHeight + valueSignSpaceHeight() + getPaddingTop() + descriptionBoxTopHeight() + getXtop());

        Point[] pointArrays = new Point[]{new Point(rectBounds.left, rectBounds.top),
                new Point(rectBounds.right, rectBounds.bottom)};
        LinearShader shader = new LinearShader(pointArrays, null,
                new Color[]{new Color(segmentBgColors[0]), new Color(segmentBgColors[1])}, Shader.TileMode.MIRROR_TILEMODE);
        emptyPaint.setShader(shader, Paint.ShaderType.LINEAR_SHADER);

        barRoundingRadius = rectBounds.getHeight() / 2;
        if (barRoundingRadius > singleSegmentWidth / 2) {
            sideStyle = SegmentedBarViewSideStyle.NORMAL;
        }

        segmentRect.modify(rectBounds);
        switch (sideStyle) {
            case SegmentedBarViewSideStyle.ROUNDED:
                roundRectangleBounds.modify(rectBounds.left, rectBounds.top, rectBounds.right, rectBounds.bottom);
                canvas.drawRoundRect(roundRectangleBounds, barRoundingRadius, barRoundingRadius, emptyPaint);
                break;
            case SegmentedBarViewSideStyle.ANGLE:
                rectBounds.set(barRoundingRadius + getPaddingLeft(),
                        valueSignSpaceHeight() + descriptionBoxTopHeight() + getPaddingTop(),
                        getWidth() - getPaddingRight() - barRoundingRadius,
                        barHeight + valueSignSpaceHeight() + descriptionBoxTopHeight() + getPaddingTop());
                canvas.drawRect(rectBounds, emptyPaint
                );
                // Draw left triangle
                point1 = new Point(rectBounds.left - barRoundingRadius, rectBounds.top + barRoundingRadius);
                point2 = new Point(rectBounds.left, rectBounds.top);
                point3 = new Point(rectBounds.left, rectBounds.bottom);

                drawTriangle(canvas, point1, point2, point3, emptyPaint);

                // Draw right triangle
                point1 = new Point(rectBounds.right + barRoundingRadius, rectBounds.top + barRoundingRadius);
                point2 = new Point(rectBounds.right, rectBounds.top);
                point3 = new Point(rectBounds.right, rectBounds.bottom);

                drawTriangle(canvas, point1, point2, point3, emptyPaint);
                break;
            case SegmentedBarViewSideStyle.NORMAL:
                canvas.drawRect(rectBounds, emptyPaint);
            default:
                break;
        }


        if (showSegmentText) {
            String textToShow;
            textToShow = emptySegmentText;
            segmentTextPaint.setTextSize(segmentTextSize);
            drawTextCentredInRectWithSides(canvas, segmentTextPaint, textToShow, segmentRect.left, segmentRect.top,
                    segmentRect.right, segmentRect.bottom);
        }
    }

    private int getContentWidth() {
        return getWidth() - getPaddingLeft() - getPaddingRight() - getXLeft() * 2;
    }

    private int getContentHeight() {
        return getHeight() - getPaddingTop() - getPaddingBottom();
    }

    private void drawSegment(Canvas canvas, Segment segment, int segmentIndex, int segmentsSize, float valueMax) {
        boolean isLeftSegment = segmentIndex == 0;
        boolean isRightSegment = segmentIndex == segmentsSize - 1;
        boolean isLeftAndRight = isLeftSegment && isRightSegment;
        float singleSegmentWidth, segmentLeft, segmentRight;
        boolean isValueCenter = false;
        if (sideRule == SegmentedBarViewSideRule.SCALE) { // 按比例分段
            float spacing = segment.getMaxValue() - segment.getMinValue();
            singleSegmentWidth = getContentWidth() * spacing / valueMax;
            float temp = getContentWidth() / valueMax * segment.getMinValue();
            segmentLeft = temp;
            if (!isLeftSegment) {
                segmentLeft += gapWidth;
            }
            segmentRight = getContentWidth() / valueMax * segment.getMaxValue();
            if (!isLeftSegment && isDrawSegmentBg) {
                RectFloat mRectF = new RectFloat();
                grapPaint.setColor(new Color(gapColor));
                mRectF.modify((int) segmentLeft + getPaddingLeft() + getXLeft(), valueSignSpaceHeight() + getPaddingTop() + descriptionBoxTopHeight() + getXtop(), (int) segmentLeft + getPaddingLeft() + getXLeft() + gapWidth,
                        barHeight + valueSignSpaceHeight() + descriptionBoxTopHeight() + getPaddingTop() + getXtop());
                canvas.drawRect(mRectF, grapPaint);
            }
        } else { // 平均分段
            singleSegmentWidth = (getContentWidth() + gapWidth) / segmentsSize - gapWidth;
            segmentLeft = (singleSegmentWidth + gapWidth) * segmentIndex;
            segmentRight = segmentLeft + singleSegmentWidth;
            if (!isLeftSegment && isDrawSegmentBg) {
                RectFloat mRectF = new RectFloat();
                grapPaint.setColor(new Color(gapColor));
                mRectF.modify((int) segmentLeft + getPaddingLeft() + getXLeft(), valueSignSpaceHeight() + getPaddingTop() + descriptionBoxTopHeight() + getXtop(), (int) segmentLeft + getPaddingLeft() + getXLeft() + gapWidth,
                        barHeight + valueSignSpaceHeight() + descriptionBoxTopHeight() + getPaddingTop() + getXtop());
                canvas.drawRect(mRectF, grapPaint);
            }
        }
        // Segment bounds
        rectBounds.set((int) segmentLeft + getPaddingLeft() + getXLeft(), valueSignSpaceHeight() + descriptionBoxTopHeight()
                + getPaddingTop() + getXtop(), (int) segmentRight + getPaddingRight() + getXLeft(), barHeight + valueSignSpaceHeight() +
                descriptionBoxTopHeight() + getPaddingTop() + getXtop());
        // Calculating value sign position
        if (valueSegment != null && valueSegment == segmentIndex) {
            valueSignCenter = segmentLeft + getPaddingLeft() + (singleSegmentWidth / 2) + getXLeft();
            currentBarColor = segment.getColor();
            isValueCenter = true;
        } else if (value != null && (value >= segment.getMinValue() && value < segment.getMaxValue() || (isRightSegment && segment.getMaxValue() == value))) {
            float valueSignCenterPercent = (value - segment.getMinValue()) / (segment.getMaxValue() - segment.getMinValue());
            valueSignCenter = (int) (segmentLeft + getPaddingLeft() + getXLeft() + valueSignCenterPercent * singleSegmentWidth);
            currentBarColor = segment.getColor();
            isValueCenter = true;
        }

        fillPaint.setColor(new Color(segment.getColor()));
        segmentRect.modify(rectBounds);
        // Drawing segment (with specific bounds if left or right)
        if (isLeftSegment || isRightSegment) {
            barRoundingRadius = rectBounds.getHeight() / 2;
            if (barRoundingRadius > singleSegmentWidth / 2) {
                sideStyle = SegmentedBarViewSideStyle.NORMAL;
            }

            switch (sideStyle) {
                case SegmentedBarViewSideStyle.ROUNDED:
                    roundRectangleBounds.modify(rectBounds.left, rectBounds.top, rectBounds.right, rectBounds.bottom);
                    canvas.drawRoundRect(roundRectangleBounds, barRoundingRadius, barRoundingRadius, fillPaint);
                    if (!isLeftAndRight) {
                        if (isLeftSegment) {
                            rectBounds.set((int) segmentLeft + barRoundingRadius + getPaddingLeft() + getXLeft(),
                                    valueSignSpaceHeight() + descriptionBoxTopHeight() + getPaddingTop() + getXtop(), (int)
                                            segmentRight + getPaddingRight() + getXLeft(), barHeight + valueSignSpaceHeight()
                                            + descriptionBoxTopHeight() + getPaddingTop() + getXtop());
                            canvas.drawRect(rectBounds, fillPaint
                            );
                        } else {
                            rectBounds.set((int) segmentLeft + getPaddingLeft() + getXLeft(), valueSignSpaceHeight()
                                    + descriptionBoxTopHeight() + getPaddingTop() + getXtop(), (int) segmentRight
                                    - barRoundingRadius + getPaddingRight() + getXLeft(), barHeight + valueSignSpaceHeight()
                                    + descriptionBoxTopHeight() + getPaddingTop() + getXtop());
                            canvas.drawRect(
                                    rectBounds,
                                    fillPaint
                            );
                        }
                    }
                    break;
                case SegmentedBarViewSideStyle.ANGLE:
                    if (isLeftAndRight) {
                        rectBounds.set((int) segmentLeft + barRoundingRadius + getPaddingLeft() + getXLeft(), valueSignSpaceHeight()
                                + descriptionBoxTopHeight() + getPaddingTop() + getXtop(), (int) segmentRight
                                - barRoundingRadius + getPaddingLeft() + getXLeft(), barHeight + valueSignSpaceHeight()
                                + getPaddingTop() + descriptionBoxTopHeight() + getXtop());
                        canvas.drawRect(
                                rectBounds,
                                fillPaint
                        );
                        // Draw left triangle
                        point1 = new Point(rectBounds.left - barRoundingRadius, rectBounds.top + barRoundingRadius);
                        point2 = new Point(rectBounds.left, rectBounds.top);
                        point3 = new Point(rectBounds.left, rectBounds.bottom);

                        drawTriangle(canvas, point1, point2, point3, fillPaint);

                        // Draw right triangle
                        point1 = new Point(rectBounds.right + barRoundingRadius, rectBounds.top + barRoundingRadius);
                        point2 = new Point(rectBounds.right, rectBounds.top);
                        point3 = new Point(rectBounds.right, rectBounds.bottom);

                        drawTriangle(canvas, point1, point2, point3, fillPaint);
                    } else {
                        if (isLeftSegment) {
                            rectBounds.set((int) segmentLeft + barRoundingRadius + getPaddingLeft() + getXLeft(),
                                    valueSignSpaceHeight() + descriptionBoxTopHeight() + getPaddingTop() + getXtop(), (int)
                                            segmentRight + getPaddingLeft() + getXLeft(), barHeight + valueSignSpaceHeight()
                                            + descriptionBoxTopHeight() + getPaddingTop() + getXtop());
                            canvas.drawRect(
                                    rectBounds,
                                    fillPaint
                            );
                            // Draw left triangle
                            point1 = new Point(rectBounds.left - barRoundingRadius, rectBounds.top + barRoundingRadius);
                            point2 = new Point(rectBounds.left, rectBounds.top);
                            point3 = new Point(rectBounds.left, rectBounds.bottom);

                            drawTriangle(canvas, point1, point2, point3, fillPaint);
                        } else {
                            rectBounds.set((int) segmentLeft + getPaddingLeft() + getXLeft(), valueSignSpaceHeight()
                                    + descriptionBoxTopHeight() + getPaddingTop() + getXtop(), (int) segmentRight
                                    - barRoundingRadius + getPaddingLeft() + getXLeft(), barHeight + valueSignSpaceHeight()
                                    + descriptionBoxTopHeight() + getPaddingTop() + getXtop());
                            canvas.drawRect(
                                    rectBounds,
                                    fillPaint
                            );
                            // Draw right triangle
                            point1 = new Point(rectBounds.right + barRoundingRadius, rectBounds.top + barRoundingRadius);
                            point2 = new Point(rectBounds.right, rectBounds.top);
                            point3 = new Point(rectBounds.right, rectBounds.bottom);

                            drawTriangle(canvas, point1, point2, point3, fillPaint);
                        }
                    }
                    break;
                case SegmentedBarViewSideStyle.NORMAL:
                    canvas.drawRect(rectBounds, fillPaint);
                default:
                    break;
            }
        } else {
            canvas.drawRect(rectBounds, fillPaint);
        }

        // Drawing segment text
        if (showSegmentText) {
            String textToShow;
            if (segment.getCustomText() != null) {
                textToShow = segment.getCustomText();
            } else {
                if (isLeftSegment || isRightSegment) {
                    if (isLeftAndRight || sideTextStyle == SegmentedBarViewSideTextStyle.TWO_SIDED) {
                        textToShow = String.format("%s - %s", formatter.format(segment.getMinValue()), formatter
                                .format(segment.getMaxValue()));
                    } else if (isLeftSegment) {
                        textToShow = String.format("<%s", formatter.format(segment.getMaxValue()));
                    } else {
                        textToShow = String.format(">%s", formatter.format(segment.getMinValue()));
                    }
                } else {
                    textToShow = String.format("%s - %s", formatter.format(segment.getMinValue()), formatter.format(segment.getMaxValue()));
                }
            }

            segmentTextPaint.setTextSize(segmentTextSize);
            segmentTextPaint.setColor(new Color(segmentTextColor));
            drawTextCentredInRect(canvas, segmentTextPaint, textToShow, segmentRect);
        }

        // Drawing segment description text
        if (showDescriptionText) {
            descriptionTextPaint.setTextSize(isValueCenter && descriptionAlign == ALIGN_CENTER ? descriptionHightlightTextSize == -1 ? descriptionTextSize : descriptionHightlightTextSize : descriptionTextSize);
            descriptionTextPaint.setColor(new Color(isValueCenter && descriptionAlign == ALIGN_CENTER ? descriptionHighlightTextColor == 65536 ? descriptionTextColor : descriptionHighlightTextColor : descriptionTextColor));
            descriptionTextPaint.setTextSize(40);
            switch (descriptionAlign) {
                case ALIGN_CENTER:
                    drawTextCentredInRectWithSides(canvas, descriptionTextPaint, segment.getDescriptionText(),
                            segmentRect.left, segmentRect.bottom + 20, segmentRect.right, segmentRect.bottom
                                    + descriptionBoxHeight + 20);
                    break;
                case ALIGN_BOTH:
                    drawTextLeftRightInRectBothSides(canvas, descriptionTextPaint, segment.getDescriptionText(),
                            segmentRect.left, segmentRect.bottom + 20, segmentRect.right, segmentRect.bottom
                                    + descriptionBoxHeight + 20, isLeftSegment, isRightSegment, segmentIndex);
                    break;
            }
        }

        if (showDescriptionTopText) {
            descriptionTextPaint.setTextSize(isValueCenter && descriptionTopAlign == ALIGN_CENTER ? descriptionHightlightTextSize == -1 ? descriptionTextSize : descriptionHightlightTextSize : descriptionTextSize);
            descriptionTextPaint.setColor(new Color(isValueCenter && descriptionTopAlign == ALIGN_CENTER ? descriptionHighlightTextColor == 65536 ? descriptionTopTextColor : descriptionHighlightTextColor : descriptionTopTextColor));
            switch (descriptionTopAlign) {
                case ALIGN_CENTER:
                    drawTextCentredInRectWithSides(canvas, descriptionTextPaint, segment.getTopDescriptionText(),
                            segmentRect.left, segmentRect.top - descriptionBoxTopHeight(), segmentRect.right,
                            segmentRect.top);
                    break;
                case ALIGN_BOTH:
                    drawTextLeftRightInRectBothSides(canvas, descriptionTextPaint, segment.getTopDescriptionText(),
                            segmentRect.left, segmentRect.top - descriptionBoxTopHeight(), segmentRect.right,
                            segmentRect.top, isLeftSegment, isRightSegment, segmentIndex);
                    break;
            }
        }
    }

    private void drawValueSign(Canvas canvas, int valueSignSpaceHeight, int valueSignCenter) {
        boolean valueNotInSegments = valueSignCenter == -1;
        if (valueNotInSegments) {
            valueSignCenter = getContentWidth() / 2 + getPaddingLeft();
        }
        valueSignBounds.set(valueSignCenter - valueSignWidth / 2,
                getPaddingTop(),
                valueSignCenter + valueSignWidth / 2,
                valueSignHeight - arrowHeight + getPaddingTop());
        signPaint.setColor(new Color(valueSignColor));

        // Move if not fit horizontal
        if (valueSignBounds.left < getPaddingLeft()) {
            int difference = -valueSignBounds.left + getPaddingLeft();
            roundRectangleBounds.modify(valueSignBounds.left + difference, valueSignBounds.top, valueSignBounds.right
                    + difference, valueSignBounds.bottom);
        } else if (valueSignBounds.right > /*getMeasuredWidth()*/ getEstimatedWidth() - getPaddingRight()) {
            int difference = valueSignBounds.right - /*getMeasuredWidth()*/getEstimatedWidth() + getPaddingRight();
            roundRectangleBounds.modify(valueSignBounds.left - difference, valueSignBounds.top, valueSignBounds.right
                    - difference, valueSignBounds.bottom);
        } else {
            roundRectangleBounds.modify(valueSignBounds.left, valueSignBounds.top, valueSignBounds.right,
                    valueSignBounds.bottom);
        }

        canvas.drawRoundRect(
                roundRectangleBounds,
                valueSignRound,
                valueSignRound,
                signPaint
        );
        if (show_sign_boder) {
            canvas.drawRoundRect(
                    roundRectangleBounds,
                    valueSignRound,
                    valueSignRound,
                    signborderPaint
            );
        }

        // Draw arrow
        if (!valueNotInSegments) {
            int difference = 0;
            if (valueSignCenter - arrowWidth / 2 < barRoundingRadius + getPaddingLeft()) {
                difference = barRoundingRadius - valueSignCenter + getPaddingLeft();
            } else if (valueSignCenter + arrowWidth / 2 > getEstimatedWidth() - barRoundingRadius - getPaddingRight()) {
                difference = (getEstimatedWidth() - barRoundingRadius) - valueSignCenter - getPaddingRight();
            }

            point1 = new Point(valueSignCenter - arrowWidth / 2 + difference, valueSignSpaceHeight - arrowHeight
                    + getPaddingTop());
            point2 = new Point(valueSignCenter + arrowWidth / 2 + difference, valueSignSpaceHeight - arrowHeight
                    + getPaddingTop());
            point3 = new Point(valueSignCenter + difference, valueSignSpaceHeight + getPaddingTop());

            drawTriangle(canvas, point1, point2, point3, signPaint);
            if (show_sign_boder) {
                drawTriangleBoder(canvas, point1, point2, point3, signborderPaint);
            }
        }

        // Draw value text
        if (valueTextLayout != null) {
            canvas.translate(roundRectangleBounds.left + roundRectangleBounds.getWidth() / 2 - (float) valueTextLayout.getWidth() / 2, roundRectangleBounds.top + roundRectangleBounds.getHeight() / 2
                    - (float) valueTextLayout.getHeight() / 2);
            valueTextLayout.drawText(canvas);
        }
    }

    private void drawTriangle(Canvas canvas, Point point1, Point point2, Point point3, Paint paint) {
        trianglePath.reset();
        trianglePath.moveTo(point1.getPointX(), point1.getPointY());
        trianglePath.lineTo(point2.getPointX(), point2.getPointY());
        trianglePath.lineTo(point3.getPointX(), point3.getPointY());
        trianglePath.lineTo(point1.getPointX(), point1.getPointY());
        trianglePath.close();

        canvas.drawPath(trianglePath, paint);
    }

    /**
     * 将三角形的一条顶边用颜色给覆盖掉
     *
     * @param canvas canvas
     * @param paint  paint
     * @param point1 point1
     * @param point2 point2
     * @param point3 point3
     */
    private void drawTriangleBoder(Canvas canvas, Point point1, Point point2, Point point3, Paint paint) {
        triangleboderPath.reset();
        triangleboderPath.moveTo(point1.getPointX(), point1.getPointY());
        triangleboderPath.lineTo(point2.getPointX(), point2.getPointY());
        paint.setColor(fillPaint.getColor());
        paint.setStrokeWidth(value_sign_border_size + 1f);
        canvas.drawPath(triangleboderPath, paint);
        triangleboderPath.reset();
        paint.setStrokeWidth(value_sign_border_size);
        float value = (float) value_sign_border_size / 6;
        triangleboderPath.moveTo(point1.getPointX() - value, point1.getPointY() - value);
        triangleboderPath.lineTo(point3.getPointX(), point3.getPointY());
        triangleboderPath.lineTo(point2.getPointX() + value, point2.getPointY() - value);
        paint.setColor(new Color(value_sign_border_color));
        canvas.drawPath(triangleboderPath, paint);
    }

    @Override
    public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
        int minWidth = getPaddingLeft() + getPaddingRight();
        int height = barHeight;
        if (sliderType == CUSTOM || sliderType == SLIDER) {
            height = Math.max(barHeight, thembH);
        }
        int minHeight = height + getPaddingBottom() + getPaddingTop();
        if (!valueIsEmpty()) {
            minHeight += valueSignHeight + arrowHeight;
        }
        if (showDescriptionText) {
            minHeight += descriptionBoxHeight;
        }
        if (showDescriptionTopText) {
            minHeight += descriptionBoxTopHeight;
        }
        int width1 = minWidth;
        int height1 = minHeight;
        setEstimatedSize(width1, height1);
        return false;
    }

    private int valueSignSpaceHeight() {
        if (valueIsEmpty()) return 0;
        return valueSignHeight;
    }

    private int descriptionBoxTopHeight() {
        if (valueIsEmpty()) return 0;
        return descriptionBoxTopHeight;
    }

    private boolean valueIsEmpty() {
        return value == null && valueSegment == null;
    }

    private void drawTextCentredInRect(Canvas canvas, Paint paint, String text, Rect outsideRect) {
        drawTextCentredInRectWithSides(canvas, paint, text, outsideRect.left, outsideRect.top, outsideRect.right,
                outsideRect.bottom);
    }

    /**
     * 自己扩展了可以支持左右的下标
     *
     * @param paint          paint
     * @param canvas         canvas
     * @param bottom         bottom
     * @param index          index
     * @param isLeftSegment  isLeftSegment
     * @param isRightSegment isRightSegment
     * @param left           left
     * @param right          right
     * @param text           text
     * @param top            top
     */
    private void drawTextLeftRightInRectBothSides(Canvas canvas, Paint paint, String text, float left, float top,
                                                  float right, float bottom, boolean isLeftSegment, boolean
                                                          isRightSegment, int index) {
        paint.setTextAlign(TextAlignment.CENTER);
        float textHeight = paint.descent() - paint.ascent();
        float textOffset = (textHeight / 2) - paint.descent();
        float textWidth = paint.measureText(text);
        if (isLeftSegment) {
            canvas.drawText(paint, text, left + textWidth / 2, (top + bottom) / 2 + textOffset);
            return;
        }
        if (isRightSegment) {
            if (segments.size() <= 2) {
                if (text.contains("&")) { // 必须是&分割
                    String[] texts = split(text, "&");
                    canvas.drawText(paint, texts[0], left, (top + bottom) / 2 + textOffset);
                    float rightTextWidth = paint.measureText(texts[1]);
                    canvas.drawText(paint, texts[1], right - rightTextWidth / 2, (top + bottom) / 2 + textOffset);
                    return;
                }
            }
            canvas.drawText(paint, text, right - textWidth / 2, (top + bottom) / 2 + textOffset);
            return;
        }
        if (!isLeftSegment && !isRightSegment) { // 如果不是在两端  对2求余
            if (text.contains("&")) { // 必须是&分割
                String[] texts = split(text, "&");
                if (index % 2 != 0) {
                    canvas.drawText(paint, texts[0], left, (top + bottom) / 2 + textOffset);
                    canvas.drawText(paint, texts[1], right, (top + bottom) / 2 + textOffset);
                } else {
                    canvas.drawText(paint, texts[1], right, (top + bottom) / 2 + textOffset);
                }
            }
        } else {
            canvas.drawText(paint, text, right - textWidth / 2, (top + bottom) / 2 + textOffset);
        }
    }

    /**
     * 分割
     *
     * @param text       text
     * @param expression expression
     * @return split
     */
    public static String[] split(String text, String expression) {
        if (text.length() == 0) {
            return EMPTY_STRING_ARRAY;
        } else {
            return text.split(expression, -1);
        }
    }

    private void drawTextCentredInRectWithSides(Canvas canvas, Paint paint, String text, float left, float top, float
            right, float bottom) {
        paint.setTextAlign(TextAlignment.CENTER);

        float textHeight = paint.descent() - paint.ascent();
        float textOffset = (textHeight / 2) - paint.descent();
        if (TextTool.isNullOrEmpty(text)) return;
        canvas.drawText(paint, text, (left + right) / 2, (top + bottom) / 2 + textOffset);
    }

    private void createValueTextLayout() {
        if (valueIsEmpty()) {
            valueTextLayout = null;
            return;
        }
        String text = value != null ? formatter.format(value) : valueSegmentText;
        if (value != null && unit != null && !unit.isEmpty())
            text += String.format(" <small>%s</small>", unit);
        if (!TextTool.isNullOrEmpty(valueText)) {
            text = valueText;
        }
        Rect rect = new Rect(0, 0, getWidth(), getHeight());
        valueTextLayout = new SimpleTextLayout(text, valueTextPaint, rect, valueSignWidth, false);
    }

    /**
     * getValueSegmentText
     *
     * @return getValueSegmentText
     */
    public String getValueSegmentText() {
        return valueSegmentText;
    }

    /**
     * setValueSegmentText
     *
     * @param valueSegmentText valueSegmentText
     */
    public void setValueSegmentText(String valueSegmentText) {
        this.valueSegmentText = valueSegmentText;
        createValueTextLayout();
        invalidate();
        postLayout();
    }

    /**
     * setSegments
     *
     * @param segments segments
     */
    public void setSegments(List<Segment> segments) {
        this.segments = segments;
        invalidate();
        postLayout();
    }

    /**
     * setUnit
     *
     * @param unit unit
     */
    public void setUnit(String unit) {
        this.unit = unit;
        createValueTextLayout();
        invalidate();
        postLayout();
    }

    /**
     * setValue
     *
     * @param value setValue
     */
    public void setValue(Float value) {
        this.value = value;
        createValueTextLayout();
        invalidate();
        postLayout();
    }

    /**
     * setValueWithUnit
     *
     * @param value value
     * @param unitHtml unitHtml
     */
    public void setValueWithUnit(Float value, String unitHtml) {
        this.value = value;
        this.unit = unitHtml;
        if (!valueIsEmpty()) {
            createValueTextLayout();
        }
        invalidate();
        postLayout();
    }

    /**
     * setGapWidth
     *
     * @param gapWidth gapWidth
     */
    public void setGapWidth(int gapWidth) {
        this.gapWidth = gapWidth;
        invalidate();
        postLayout();
    }

    /**
     * 在设置画背景时，开启了isDrawSegmentBg属性，才会起作用,正常情况下只是设置间隔空白，不会给间隔设置颜色（只适用于背景渐变分段的特殊情况）
     *
     * @param gapColor gapColor
     */
    public void setGapColor(int gapColor) {
        this.gapColor = gapColor;
        invalidate();
        postLayout();
    }

    /**
     * setBarHeight
     *
     * @param barHeight barHeight
     */
    public void setBarHeight(int barHeight) {
        this.barHeight = barHeight;
        invalidate();
        postLayout();
    }

    /**
     * setShowDescriptionText
     *
     * @param showDescriptionText showDescriptionText
     */
    public void setShowDescriptionText(boolean showDescriptionText) {
        this.showDescriptionText = showDescriptionText;
        invalidate();
        postLayout();
    }

    /**
     * setValueSignSize
     *
     * @param width width
     * @param height height
     */
    public void setValueSignSize(int width, int height) {
        this.valueSignWidth = width;
        this.valueSignHeight = height;
        if (!valueIsEmpty()) {
            createValueTextLayout();
        }
        invalidate();
        postLayout();
    }

    /**
     * setValueSignColor
     *
     * @param valueSignColor valueSignColor
     */
    public void setValueSignColor(int valueSignColor) {
        this.valueSignColor = valueSignColor;
        invalidate();
        postLayout();
    }

    /**
     * setShowSegmentText
     *
     * @param showSegmentText showSegmentText
     */
    public void setShowSegmentText(boolean showSegmentText) {
        this.showSegmentText = showSegmentText;
        invalidate();
        postLayout();
    }

    /**
     * setSideStyle
     *
     * @param sideStyle setSideStyle
     */
    public void setSideStyle(int sideStyle) {
        this.sideStyle = sideStyle;
        invalidate();
        postLayout();
    }

    /**
     * setEmptySegmentColor
     *
     * @param emptySegmentColor emptySegmentColor
     */
    public void setEmptySegmentColor(int emptySegmentColor) {
        this.emptySegmentColor = emptySegmentColor;
        invalidate();
        postLayout();
    }

    /**
     * 是否显示渐变背景
     *
     * @param drawSegmentBg drawSegmentBg
     */
    public void setDrawSegmentBg(boolean drawSegmentBg) {
        this.isDrawSegmentBg = drawSegmentBg;
        invalidate();
        postLayout();
    }

    /**
     * 设置渐变背景
     *
     * @param startColor 起始颜色
     * @param endColor   结束颜色
     */
    public void setGradientBgSegmentColor(int startColor, int endColor) {
        this.segmentBgColors[0] = startColor;
        this.segmentBgColors[1] = endColor;
        invalidate();
        postLayout();
    }

    /**
     * 只有按照比例分段才有进度
     *
     * @param startColor startColor
     * @param endColor   endColor
     */
    public void setGradientProgressSegmentColor(int startColor, int endColor) {
        this.segmentProgressColors[0] = startColor;
        this.segmentProgressColors[1] = endColor;
        invalidate();
        postLayout();
    }

    /**
     * 设置滑块显示的字体样式
     *
     * @param sideTextStyle sideTextStyle
     */
    public void setSideTextStyle(int sideTextStyle) {
        this.sideTextStyle = sideTextStyle;
        invalidate();
        postLayout();
    }

    /**
     * 设置描述的文本字体大小
     *
     * @param descriptionTextSize descriptionTextSize
     */
    public void setDescriptionTextSize(int descriptionTextSize) {
        this.descriptionTextSize = descriptionTextSize;
        invalidate();
        postLayout();
    }

    /**
     * 设置分段控件上文本字体大小
     *
     * @param segmentTextSize segmentTextSize
     */
    public void setSegmentTextSize(int segmentTextSize) {
        this.segmentTextSize = segmentTextSize;
        invalidate();
        postLayout();
    }

    /**
     * 设置滑块的字体大小
     *
     * @param valueTextSize valueTextSize1
     */
    public void setValueTextSize(int valueTextSize) {
        this.valueTextSize = valueTextSize;
        valueTextPaint.setTextSize(valueTextSize);
        createValueTextLayout();
        invalidate();
        postLayout();
    }

    /**
     * 设置描述的文本字体颜色
     *
     * @param descriptionTextColor descriptionTextColor
     */
    public void setDescriptionTextColor(int descriptionTextColor) {
        this.descriptionTextColor = descriptionTextColor;
        invalidate();
        postLayout();
    }

    /**
     * 设置描述顶部文本颜色
     *
     * @param descriptionTopTextColor descriptionTopTextColor
     */
    public void setDescriptionTopTextColor(int descriptionTopTextColor) {
        this.descriptionTopTextColor = descriptionTopTextColor;
        invalidate();
        postLayout();
    }

    /**
     * 设置显示的数值内容
     *
     * @param value     控件的进度
     * @param valueText //控件滑块显示的内容
     */
    public void setValue(float value, String valueText) {
        this.valueText = valueText;
        this.value = value;
        createValueTextLayout();
        invalidate();
        postLayout();
    }

    /**
     * bar条上显示文字的颜色
     *
     * @param segmentTextColor segmentTextColor
     */
    public void setSegmentTextColor(int segmentTextColor) {
        this.segmentTextColor = segmentTextColor;
        invalidate();
        postLayout();
    }

    /**
     * 设置滑块上显示文字的颜色
     *
     * @param valueTextColor valueTextColor
     */
    public void setValueTextColor(int valueTextColor) {
        this.valueTextColor = valueTextColor;
        valueTextPaint.setColor(new Color(valueTextColor));
        invalidate();
        postLayout();
    }

    /**
     * 设置显示描述文字TextVIew
     *
     * @param descriptionBoxHeight descriptionBoxHeight
     */
    public void setDescriptionBoxHeight(int descriptionBoxHeight) {
        this.descriptionBoxHeight = descriptionBoxHeight;
        invalidate();
        postLayout();
    }


    public Integer getValueSegment() {
        return valueSegment;
    }

    public void setValueSegment(Integer valueSegment) {
        this.sideRule = SegmentedBarViewSideRule.AVERAGE; // 强制等比例分
        this.valueSegment = valueSegment;
        invalidate();
        postLayout();
    }

    /**
     * 设置分段规则：等比分配，比例分配
     *
     * @param sideRule sideRule
     */
    public void setSegmentSideRule(int sideRule) {
        this.sideRule = sideRule;
        invalidate();
        postLayout();
    }

    /**
     * 获取当前bar条所在位置的颜色
     *
     * @return getCurrentBarColor
     */
    public int getCurrentBarColor() {
        return currentBarColor;
    }

    /**
     * 根据fraction值来计算当前的颜色。 fraction值范围  0f-1f
     *
     * @param endColor   endColor
     * @param fraction   fraction
     * @param startColor startColor
     * @return getCurrentColor
     */
    private int getCurrentColor(float fraction, int startColor, int endColor) {
        int redCurrent;
        int blueCurrent;
        int greenCurrent;
        int alphaCurrent;

        int redStart = new Color(startColor).getValue();
        int blueStart = new Color(startColor).getValue();
        int greenStart = new Color(startColor).getValue();
        int alphaStart = Color.alpha(startColor);

        int redEnd = new Color(endColor).getValue();
        int blueEnd = new Color(endColor).getValue();
        int greenEnd = new Color(endColor).getValue();
        int alphaEnd = Color.alpha(endColor);

        int redDifference = redEnd - redStart;
        int blueDifference = blueEnd - blueStart;
        int greenDifference = greenEnd - greenStart;
        int alphaDifference = alphaEnd - alphaStart;

        redCurrent = (int) (redStart + fraction * redDifference);
        blueCurrent = (int) (blueStart + fraction * blueDifference);
        greenCurrent = (int) (greenStart + fraction * greenDifference);
        alphaCurrent = (int) (alphaStart + fraction * alphaDifference);

        return Color.argb(alphaCurrent, redCurrent, greenCurrent, blueCurrent);
    }

    public static Builder builder(Context context) {
        return new SegmentedBarView(context).new Builder();
    }

    /**
     * Builder
     *
     * @since 2021-08-15
     */
    public class Builder {

        private Builder() {
        }

        /**
         * segments
         *
         * @param segments segments
         * @return segments
         */
        public Builder segments(List<Segment> segments) {
            SegmentedBarView.this.segments = segments;
            return this;
        }

        /**
         * unit
         *
         * @param unit unit
         * @return unit
         */
        public Builder unit(String unit) {
            SegmentedBarView.this.unit = unit;
            SegmentedBarView.this.createValueTextLayout();
            return this;
        }

        /**
         * value
         *
         * @param value value
         * @return value
         */
        public Builder value(Float value) {
            SegmentedBarView.this.value = value;
            SegmentedBarView.this.createValueTextLayout();
            return this;
        }

        /**
         * valueSegment
         *
         * @param valueSegment valueSegment
         * @return valueSegment
         */
        public Builder valueSegment(Integer valueSegment) {
            SegmentedBarView.this.valueSegment = valueSegment;
            SegmentedBarView.this.createValueTextLayout();
            return this;
        }

        /**
         * valueSegmentText
         *
         * @param valueSegmentText valueSegmentText
         * @return valueSegmentText
         */
        public Builder valueSegmentText(String valueSegmentText) {
            SegmentedBarView.this.valueSegmentText = valueSegmentText;
            SegmentedBarView.this.createValueTextLayout();
            return this;
        }

        /**
         * gapWidth
         *
         * @param gapWidth gapWidth
         * @return gapWidth
         */
        public Builder gapWidth(int gapWidth) {
            SegmentedBarView.this.gapWidth = gapWidth;
            return this;
        }

        /**
         * barHeight
         *
         * @param barHeight barHeight
         * @return barHeight
         */
        public Builder barHeight(int barHeight) {
            SegmentedBarView.this.barHeight = barHeight;
            return this;
        }

        /**
         * showDescriptionText
         *
         * @param showDescriptionText showDescriptionText
         * @return showDescriptionText
         */
        public Builder showDescriptionText(boolean showDescriptionText) {
            SegmentedBarView.this.showDescriptionText = showDescriptionText;
            return this;
        }

        /**
         * valueSignSize
         *
         * @param width width
         * @param height height
         * @return valueSignSize
         */
        public Builder valueSignSize(int width, int height) {
            SegmentedBarView.this.valueSignWidth = width;
            SegmentedBarView.this.valueSignHeight = height;
            return this;
        }

        /**
         * valueSignColor
         *
         * @param valueSignColor valueSignColor
         * @return valueSignColor
         */
        public Builder valueSignColor(int valueSignColor) {
            SegmentedBarView.this.valueSignColor = valueSignColor;
            return this;
        }

        /**
         * showSegmentText
         *
         * @param showText showText
         * @return showSegmentText
         */
        public Builder showSegmentText(boolean showText) {
            SegmentedBarView.this.showSegmentText = showText;
            return this;
        }

        /**
         * sideStyle
         *
         * @param sideStyle sideStyle
         * @return sideStyle
         */
        public Builder sideStyle(int sideStyle) {
            SegmentedBarView.this.sideStyle = sideStyle;
            return this;
        }

        /**
         * emptySegmentColor
         *
         * @param emptySegmentColor emptySegmentColor
         * @return emptySegmentColor
         */
        public Builder emptySegmentColor(int emptySegmentColor) {
            SegmentedBarView.this.emptySegmentColor = emptySegmentColor;
            return this;
        }

        /**
         * sideTextStyle
         *
         * @param sideTextStyle sideTextStyle
         * @return sideTextStyle
         */
        public Builder sideTextStyle(int sideTextStyle) {
            SegmentedBarView.this.sideTextStyle = sideTextStyle;
            return this;
        }

        /**
         * descriptionTextSize
         *
         * @param descriptionTextSize descriptionTextSize
         * @return descriptionTextSize
         */
        public Builder descriptionTextSize(int descriptionTextSize) {
            SegmentedBarView.this.descriptionTextSize = descriptionTextSize;
            return this;
        }

        /**
         * segmentTextSize
         *
         * @param segmentTextSize segmentTextSize
         * @return segmentTextSize
         */
        public Builder segmentTextSize(int segmentTextSize) {
            SegmentedBarView.this.segmentTextSize = segmentTextSize;
            return this;
        }

        /**
         * valueTextSize
         *
         * @param valueTextSize valueTextSize
         * @return valueTextSize
         */
        public Builder valueTextSize(int valueTextSize) {
            SegmentedBarView.this.valueTextSize = valueTextSize;
            return this;
        }

        /**
         * descriptionTextColor
         *
         * @param descriptionTextColor descriptionTextColor
         * @return descriptionTextColor
         */
        public Builder descriptionTextColor(int descriptionTextColor) {
            SegmentedBarView.this.descriptionTextColor = descriptionTextColor;
            return this;
        }

        /**
         * descriptionTopTextColor
         *
         * @param descriptionTopTextColor descriptionTopTextColor
         * @return descriptionTopTextColor
         */
        public Builder descriptionTopTextColor(int descriptionTopTextColor) {
            SegmentedBarView.this.descriptionTopTextColor = descriptionTopTextColor;
            return this;
        }

        /**
         * segmentTextColor
         *
         * @param segmentTextColor segmentTextColor
         * @return segmentTextColor
         */
        public Builder segmentTextColor(int segmentTextColor) {
            SegmentedBarView.this.segmentTextColor = segmentTextColor;
            return this;
        }

        /**
         * valueTextColor
         *
         * @param valueTextColor valueTextColor
         * @return valueTextColor
         */
        public Builder valueTextColor(int valueTextColor) {
            SegmentedBarView.this.valueTextColor = valueTextColor;
            return this;
        }

        /**
         * descriptionBoxHeight
         *
         * @param descriptionBoxHeight descriptionBoxHeight
         * @return descriptionBoxHeight
         */
        public Builder descriptionBoxHeight(int descriptionBoxHeight) {
            SegmentedBarView.this.descriptionBoxHeight = descriptionBoxHeight;
            return this;
        }

        /**
         * sideRule
         *
         * @param sideRule sideRule
         * @return sideRule
         */
        public Builder sideRule(int sideRule) {
            SegmentedBarView.this.sideRule = sideRule;
            return this;
        }

        /**
         * build
         *
         * @return build
         */
        public SegmentedBarView build() {
            return SegmentedBarView.this;
        }

    }

    /**
     * 设置值符号的角度
     *
     * @param roundValue roundValue
     */
    public void setValueSignRound(int roundValue) {
        this.valueSignRound = roundValue;
        invalidate();
        postLayout();
    }

    /**
     * 设置滑块剪头的高度
     *
     * @param height height
     */
    public void setArrowHeight(int height) {
        this.arrowHeight = height;
        invalidate();
        postLayout();
    }

    /**
     * 设置滑块的宽度
     *
     * @param width width
     */
    public void setArrowWidth(int width) {
        this.arrowWidth = width;
        invalidate();
        postLayout();
    }
}
